<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8"/>
<title>同 Ioc 容器一起工作</title><link href="../zdoc.css" rel="stylesheet" type="text/css"/><link href="../_rs/site.css" rel="stylesheet" type="text/css"/><script src="../_rs/jquery.js" language="Javascript"></script><script src="../_rs/site.js" language="Javascript"></script><script src="../_rs/z.js" language="Javascript"></script>
</head>
<body><a name="top"></a>
<div class="zdoc_header">同 Ioc 容器一起工作</div>
<div class="zdoc_author"><em>By:</em><b>zozoh</b><a href="mailto:zozohtnt@gmail.com">&lt;zozohtnt@gmail.com&gt;</a></div>
<div class="zdoc_body">
<ul class="zdoc_index_table">
<li>
<div><span class="num">1</span><a href="#内置的_Ioc_容器">内置的 Ioc 容器</a></div>
</li>
<li>
<div><span class="num">2</span><a href="#JSON_配置的自动扫描">JSON 配置的自动扫描</a></div>
</li>
<li>
<div><span class="num">3</span><a href="#由_Ioc_容器管理子模块">由 Ioc 容器管理子模块</a></div>
</li>
<li>
<div><span class="num">4</span><a href="#在容器对象里获得_ServletContext">在容器对象里获得 ServletContext</a></div>
</li>
<li>
<div><span class="num">5</span><a href="#容器管理对象的生命周期范围">容器管理对象的生命周期范围</a></div>
</li>
<li>
<div><span class="num">6</span><a href="#需要注意的问题">需要注意的问题</a></div>
</li>
<li>
<div><span class="num">7</span><a href="#自定_Ioc_容器">自定 Ioc 容器</a></div>
</li>
</ul>
<div class="hr"><b></b></div>
<h1><a name="内置的_Ioc_容器"></a>内置的 Ioc 容器</h1>
<div style="float:right;"><a href="#top">Top</a></div>
<p>一个 Mvc 框架可以通过 Ioc 接口同一个 Ioc 容器挂接，挂接的方法很简单： 在主模块上声明 @IocBy</p>
<pre>@IocBy(type=JsonIocProvider.class, args={"/conf/core.js", "/conf/pet.js"})
public class MainModule {
	...
</pre>
<p>Nutz.Mvc  内置了 JsonIocProvider 类，帮助你同标准 NutIoc 容器挂接。下面是这个类的全部源代码：</p>
<pre>public class JsonIocProvider implements IocProvider {
	public Ioc create(ServletConfig config, String[] args) {
		return new NutIoc(new JsonLoader(args), new ScopeContext("app"), "app");
	}
}
</pre>
<p>极其简单，对吗？ 其中，@IocBy 的 "args" 属性的值将会直接传入 IocProvider 的 create 函数。</p>
<div class="hr"><b></b></div>
<h1><a name="JSON_配置的自动扫描"></a>JSON 配置的自动扫描</h1>
<div style="float:right;"><a href="#top">Top</a></div>
<p>通常你的应用会有不只一个 ioc 配置文件，如果一个一个的写出来非常麻烦。所以，如果你指定的是一个目录：</p>
<pre>@IocBy(type=JsonIocProvider.class, args={"myioc"})
public class MainModule {
	...
</pre>
<p>如上例，那么， Nutz 内置的 JsonLoader 会自动寻找 myioc 的目录（除了 CLASSPATH 下的目录，也可以是个绝对目录）目录内的所有 *.js 或者 *.json 文件都会被自动加载</p>
<p>并且，你也可以混合目录和文件，比如：</p>
<pre>@IocBy(type=JsonIocProvider.class, args={"myioc", "abc/single.js"})
public class MainModule {
	...
</pre>
<p>目录 "myioc" 下所有的 *.js 和 *.json 文件都会被加载，并且也会加载 "abc/single.js"</p>
<div class="hr"><b></b></div>
<h1><a name="由_Ioc_容器管理子模块"></a>由 Ioc 容器管理子模块</h1>
<div style="float:right;"><a href="#top">Top</a></div>
<p>通过 @IocBy 为整个应用声明了 Ioc 容器，那么如何使用呢。 实际上，你的每一个模块都可以来自容器，只要你在模块上声明 @InjectName。 当然，在主模块声明这个注解是没有意义的。</p>
<pre>@InjectName("petM")
public class PetModule {
   ...
</pre>
<p></p>
<ul type="disc">
<li>如果你声明了这个注解， Nutz.Mvc 构造你的这个模块的时候，会通过 Ioc 容器获取，而不直接调用默认构造函数了。</li>
<li>如果你的 '@InjectName' 并没有值，那么默认会将你的模块类名首字母小写作为模块的注入名
<ul type="circle">
<li>比如上例，你直接声明 '@InjectName' 同 '@InjectName("petModule")' 是等效的</li>
</ul>
</li>
</ul>
<div class="hr"><b></b></div>
<h1><a name="在容器对象里获得_ServletContext"></a>在容器对象里获得 ServletContext</h1>
<div style="float:right;"><a href="#top">Top</a></div>
<p>ServletContext! 是的，有些时候你需要它。比如你打算作一个 Freemarker 的工厂。总之 Java 世界的奇奇怪怪的框架和插件们或多或少的都可能会依赖这个接口。如果你正在读这段文字，你说不定也正好需要这个接口。</p>
<p>在 Nutz.Mvc 中，你可以很容易在入口函数里拿到 ServletContext，是的，你只要直接在入口函数的参数里声明 ServletContext类型的参数就是了，Nutz.Mvc 会老老实实的为你填充这个参数，同理你也能拿到 HttpSession, HttpServletRequest, HttpServletResponse。但是在 Ioc 容器里，你希望你的对象也能得到这个参数，这个要求很过分吗？不当然不过分，在 1.a.33之后的版本，Nutz.Mvc 在启动的时候，会为你的 Ioc 容器增加一个新的自定义值，在容器里，你可以：</p>
<pre>...
fields : {
    servletContext : {app:'$servlet'}
}
...
</pre>
<p>你就能为你这个对象的 servletContext 字段注入 ServletContext 的实例。当然，如果你想获得 ServletContext 中的属性，你完全可以：</p>
<pre>...
fields : {
    servletContext : {app:'myObjName'}
}
...
</pre>
<p>那么，你在 servletContext 里的属性也会被一并注入</p>
<p>当然，你的 Ioc 容器，必须实现了 Ioc2 这个接口，默认的 NutIoc 就是实现这个接口的。</p>
<div class="hr"><b></b></div>
<h1><a name="容器管理对象的生命周期范围"></a>容器管理对象的生命周期范围</h1>
<div style="float:right;"><a href="#top">Top</a></div>
<p>如果你使用的是 Nutz.Ioc 标准容器，或者你的 Ioc 容器实现了 Ioc2 接口，那么你的模块类的生命周期是可以定制的。以 Nutz 的 Json 配置语法为例，你可以为你的对象增加属性：</p>
<pre>{
	petModule : {
		type : "com.my.PetModule",
		scope : "session"
	}
}
</pre>
<p>那么，你的 petModule 对象将只会存在 session里，当 session 停止后，会被 NutSessionListener 注销。当然，你需要在web.xml 中声明这个会话监听器。</p>
<p>对于 Nutz.Mvc，它支持如下的生命周期范围</p>
<ul type="disc">
<li>app</li>
<li>session</li>
<li>request</li>
</ul>
<div class="hr"><b></b></div>
<h1><a name="需要注意的问题"></a>需要注意的问题</h1>
<div style="float:right;"><a href="#top">Top</a></div>
<p>如果你让你的 Mvc 框架同 Ioc 容器一起工作 （通常你都会这样），请注意，有可能会有这样的问题 (Issue 105)：</p>
<p>你的模块的 @InjectName 或者 Ioc 配置文件里的对象的名称，如果同 request 或者 session 里属性名称重复，有可能会被覆盖，尤其是 @InjectName。Nutz.Mvc 的工作机制导致这个现象发生：</p>
<p>即，当服务器收到一个请求后，Nutz 会构建两个对象：</p>
<ol type="1">
<li>RequestIocContext</li>
<li>SessionIocContext</li>
</ol>
<p>并且将其合并为一个 ComboContext 传入 Ioc 容器。（当然，如果你的 Ioc 容器不是 Nutz 内置，而是自己实现的，并且你的容器没有实现 Ioc2 接口，这个问题不会发生）在调用相应模块的入口函数时，Nutz 首先将模块对象从 Ioc 容器中取出，取出的顺序是：</p>
<ol type="1">
<li>RequestIocContext</li>
<li>SessionIocContext</li>
<li>Ioc 容器内置的 Context -- ScopeContext -- scope: 'app'</li>
</ol>
<p>如果你的模块声明了注解 @InjectName("ABC") -- 事实上，你通常都会声明这个注解。但是不幸的（或者幸运的）是，你的 request或者 session 对象，在某一次操作中，被你设置了 "ABC" 属性，那么下次调用是，Ioc 容器会说：“ABC? 原来在 request 已经有了哦，那就不用我缓存里的了。”</p>
<p>这个特性给予应用程序极大的灵活性，如果愿意，你完全可以在一个session里保存一个数据源，然后在session注销时关闭这个数据源。当然，你的 Ioc 配置，一定要把引用到这个数据源的对象 singleton 都设为 false，或者将他们的 scope 都设成 session 或者 request</p>
<div class="hr"><b></b></div>
<h1><a name="自定_Ioc_容器"></a>自定 Ioc 容器</h1>
<div style="float:right;"><a href="#top">Top</a></div>
<p>你很喜欢 Spring，或者你很喜欢 Guice。不管怎么说，你不想用 Nutz.Ioc，那么没关系，你可以自己实现一个 IocProvider。并用 @IocBy 声明到整个应用中即可。通过 Spring 或者 Guice 实现 Ioc 接口想必不是什么难事。</p>
</div>
<div class="zdoc_footer"><em>By:</em><b>zozoh</b><a href="mailto:zozohtnt@gmail.com">&lt;zozohtnt@gmail.com&gt;</a></div>
</body>
</html>